package handler

import (
	"context"
	"fmt"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/golang/protobuf/ptypes/wrappers"
	"math"
	"time"

	"kop/framework/broker"
	context2 "kop/framework/context"
	"kop/framework/errors"
	"kop/libs/database"
	"kop/libs/redis"
	"kop/modules/guild/configuration"
	"kop/modules/guild/entity"
	"kop/modules/guild/models"
	"kop/modules/item/consts"
	"kop/pb"
	"kop/pb/service"
	"kop/util/date"

	activityC "kop/modules/activity/client"
	awardM "kop/modules/award/models"
	captainC "kop/modules/captain/client"
	itemC "kop/modules/item/client"
	questC "kop/modules/quest/client"
	roleC "kop/modules/role/client"
)

func NewGuildServiceHandler() *GuildServiceHandler {

	var handler GuildServiceHandler
	handler.Managers = make(map[int32]*ManagerMap, 1000)

	for i := int32(1); i < 1000; i++ {
		handler.Managers[i] = &ManagerMap{Map: make(map[int32]*Manager, 1000)}
	}
	return &handler
}

type GuildServiceHandler struct {
	context2.Context
	Managers map[int32]*ManagerMap
}

func (h *GuildServiceHandler) GetGuilds(_ context.Context, req *service.ServerPageRequest, resp *pb.Guilds) error {

	resp.Guilds = models.GetGuilds(database.DB, req.ServerID, req.Page, 20)
	return nil
}

// 随机3个公会
func (h *GuildServiceHandler) RandGuilds(ctx context.Context, _ *empty.Empty, resp *pb.Guilds) error {

	var serverID = h.GetServerID(ctx)

	resp.Guilds = models.RandGuildsByUpdateDay(database.DB, serverID, 3)
	if len(resp.Guilds) == 0 {
		resp.Guilds = models.RandGuildsByUpdateDay(database.DB, serverID, 7)
	}
	if len(resp.Guilds) == 0 {
		resp.Guilds = models.RandGuilds(database.DB, serverID)
	}

	resp.CD = quitCD(h.GetRoleID(ctx))
	return nil
}

// 随机加入公会
func (h *GuildServiceHandler) RandJoin(ctx context.Context, req *service.DefaultRequest, resp *wrappers.BoolValue) error {

	var guilds []entity.Guild

	var err error
	if err = database.DB.Where(entity.Guild{ServerID: req.Value, AutoJoin: true}).
		Where("member_max > member_num").
		Where("updated_at > ?", time.Now().AddDate(0, 0, -3)).
		Order("RANDOM()").Limit(1).Find(&guilds).Error; err != nil {
		return err
	}

	if len(guilds) == 0 {
		if err = database.DB.Where(entity.Guild{ServerID: req.Value, AutoJoin: true}).
			Where("member_max > member_num").
			Where("updated_at > ?", time.Now().AddDate(0, 0, -7)).
			Order("RANDOM()").Limit(1).Find(&guilds).Error; err != nil {
			return err
		}
	}

	if len(guilds) == 0 {
		return nil
	}

	if err = h.Join(ctx, &service.GuildJoinRequest{Role: roleC.GetRole(req.RoleID), GuildID: guilds[0].GuildID}, &pb.Guild{}); err != nil {
		return err
	}

	resp.Value = true
	return nil
}

func (h *GuildServiceHandler) MyGuild(_ context.Context, req *service.GuildRequest, resp *pb.Guild) error {

	var guildID = req.GuildID
	if guildID == 0 {
		var member, ok = models.GetMember(database.DB, req.RoleID)
		if !ok {
			return nil
		} else {
			guildID = member.GuildID
		}
	}

	var manager, err = h.getManager(req.ServerID, guildID)
	if err == nil {

		manager.RLock()
		defer manager.RUnlock()

		for _, v := range manager.GetMembers() {
			if v.RoleID == req.RoleID {
				*resp = models.BuildPbGuild(manager.Guild)
				return nil
			}
		}
	}

	return nil
}

func (h *GuildServiceHandler) GetGuild(_ context.Context, req *service.GuildRequest, resp *pb.Guild) error {

	var manager, err = h.getManager(req.ServerID, req.GuildID)
	if err == nil {

		manager.RLock()
		defer manager.RUnlock()

		*resp = models.BuildPbGuild(manager.Guild)
	}

	return err
}

func (h *GuildServiceHandler) Members(_ context.Context, req *service.GuildRequest, resp *pb.GuildMembers) error {

	var manager, err = h.getManager(req.ServerID, req.GuildID)
	if err == nil {

		manager.RLock()
		defer manager.RUnlock()

		resp.GuildMembers = manager.GetMembers()
	}

	return err
}

func (h *GuildServiceHandler) GetMember(_ context.Context, req *service.GuildRequest, resp *pb.GuildMember) error {

	var manager, err = h.getManager(req.ServerID, req.GuildID)
	if err == nil {

		manager.RLock()
		defer manager.RUnlock()

		for _, member := range manager.GetMembers() {
			if member.RoleID == req.RoleID {
				*resp = *member
				return nil
			}
		}
	}

	return err
}

func (h *GuildServiceHandler) Create(_ context.Context, req *service.GuildCreateRequest, resp *pb.Guild) (err error) {

	var _, ok = models.GetMember(database.DB, req.Role.RoleID)
	if ok {
		return errors.ParamError
	}

	if err := itemC.AsyncDelItem(req.Role.RoleID, req.Role.ServerID, consts.Diamond, 500); err != nil {
		return err
	}

	var session = database.DB.Begin()
	if session.Error != nil {
		return session.Error
	}
	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()
		}
	}()

	var guild = entity.Guild{
		Name:          req.Request.Name,
		Flag:          req.Request.Flag,
		ServerID:      req.Role.ServerID,
		Power:         req.Role.Power,
		PresidentName: req.Role.Name,
		AutoJoin:      req.Request.AutoJoin,
		Declaration:   req.Request.Declaration,
		Level:         1,
		MemberNum:     1,
		MemberMax:     10,
	}

	if err = session.Create(&guild).Error; err != nil {
		return err
	}

	*resp = models.BuildPbGuild(&guild)

	var member = models.CreateMember(session, req.Role.RoleID, guild.GuildID, pb.GuildPosition_President, 0)

	if err = session.Commit().Error; err != nil {
		return err
	}

	mergeRole(member, req.Role)

	var manager = NewManager(&guild, []*pb.GuildMember{member})
	manager.Online(req.Role.RoleID)
	manager.Work(h.work)

	h.Managers[req.Role.ServerID].Add(guild.GuildID, manager)

	_ = broker.SubSubject(req.Role.RoleID, broker.JoinGuild, guild.GuildID)

	// 公会创建：提督[提督名称七个字]创建了[公会名称]，要加入的提督来报名了！
	_ = broker.ServerPublish(guild.ServerID, pb.WsCode_CodeMessage, &pb.ServerMessage{Type: 5, Name: []string{guild.PresidentName, guild.Name}})

	// 主线任务
	questC.GuildJoin(req.Role.RoleID)
	return nil
}

func (h *GuildServiceHandler) SetName(ctx context.Context, req *pb.GuildSetNameRequest, resp *pb.Guild) error {

	var guildID = h.GetGuildID(ctx)
	if guildID == 0 {
		return errors.ParamError
	}

	if len(req.Name) > 21 {
		return errors.ParamError
	}

	var manager, err = h.getManager(h.GetServerID(ctx), guildID)
	if err != nil {
		return err
	}

	manager.Lock()
	defer manager.Unlock()

	if req.Name == manager.Guild.Name && req.Flag == manager.Guild.Flag {
		// 没变化
		return nil
	}

	var guildCopy = *manager.Guild

	defer func() {
		if r := recover(); r != nil {
			*manager.Guild = guildCopy
		}
	}()

	manager.Guild.Name = req.Name
	manager.Guild.Flag = req.Flag
	models.SaveGuild(database.DB, manager.Guild)

	*resp = models.BuildPbGuild(manager.Guild)
	return nil
}

func (h *GuildServiceHandler) SetNotice(ctx context.Context, req *wrappers.StringValue, _ *empty.Empty) error {

	var guildID = h.GetGuildID(ctx)
	if guildID == 0 {
		return errors.ParamError
	}

	if len(req.Value) > 120 {
		return errors.ParamError
	}

	return database.DB.Model(&entity.Notice{}).Where("guild_id = ?", guildID).Update("notice", req.Value).Error
}

func (h *GuildServiceHandler) SetAutoJoin(ctx context.Context, req *pb.GuildSetAutoJoinRequest, resp *pb.Guild) error {

	var guildID = h.GetGuildID(ctx)
	if guildID == 0 {
		return errors.ParamError
	}

	var manager, err = h.getManager(h.GetServerID(ctx), guildID)
	if err != nil {
		return err
	}

	manager.Lock()
	defer manager.Unlock()

	if req.AutoJoin == manager.Guild.AutoJoin && req.Declaration == manager.Guild.Declaration {
		*resp = models.BuildPbGuild(manager.Guild)
		return nil
	}

	var guildCopy = *manager.Guild

	defer func() {
		if r := recover(); r != nil {
			*manager.Guild = guildCopy
		}
	}()

	manager.Guild.AutoJoin = req.AutoJoin
	manager.Guild.Declaration = req.Declaration

	models.SaveGuild(database.DB, manager.Guild)

	*resp = models.BuildPbGuild(manager.Guild)
	return nil
}

func (h *GuildServiceHandler) Join(_ context.Context, req *service.GuildJoinRequest, resp *pb.Guild) (err error) {

	var _, ok = models.GetMember(database.DB, req.Role.RoleID)
	if ok {
		return errors.New("已是公会成员")
	}

	var manager *Manager
	manager, err = h.getManager(req.Role.ServerID, req.GuildID)
	if err != nil {
		return err
	}

	var conf, _ = configuration.GetByLevel(manager.Guild.Level)

	manager.Lock()
	defer manager.Unlock()

	if int32(len(manager.GetMembers())) >= conf.MemberCount {
		return errors.New("公会成员已达上限")
	}

	var points int32 = 0
	if item := itemC.GetItem(req.Role.RoleID, consts.Guild); item != nil {
		points = item.Number
	}

	var session = database.DB.Begin()
	if session.Error != nil {
		return session.Error
	}

	var member = models.CreateMember(session, req.Role.RoleID, req.GuildID, pb.GuildPosition_Member, points)
	mergeRole(member, req.Role)

	var guildCopy = *manager.Guild

	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()

			*manager.Guild = guildCopy
			manager.DelMember(member)
		}

	}()

	manager.AddMember(member)
	models.SaveGuild(session, manager.Guild)

	if err = session.Commit().Error; err != nil {
		return err
	}

	_ = broker.SubSubject(req.Role.RoleID, broker.JoinGuild, req.GuildID)

	models.CreateRecord(manager.Guild.ServerID, manager.Guild.GuildID, 1, req.Role.Name, "")

	// 主线任务
	questC.GuildJoin(req.Role.RoleID)

	*resp = models.BuildPbGuild(manager.Guild)
	return nil
}

func (h *GuildServiceHandler) Quit(ctx context.Context, _ *empty.Empty, resp *wrappers.Int32Value) (err error) {

	var guildID = h.GetGuildID(ctx)
	if guildID == 0 {
		return errors.ParamError
	}

	manager, err := h.getManager(h.GetServerID(ctx), guildID)
	if err != nil {
		return err
	}

	manager.Lock()
	defer manager.Unlock()

	var member = manager.GetMember(h.GetRoleID(ctx))
	if member == nil {
		return nil
	}

	var guildCopy = *manager.Guild

	var session = database.DB.Begin()
	if session.Error != nil {
		return session.Error
	}
	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()

			*manager.Guild = guildCopy
			manager.AddMember(member)
		}
	}()

	manager.DelMember(member)
	models.SaveGuild(session, manager.Guild)

	if err = models.DelMember(session, member.RoleID); err != nil {
		return err
	}

	if err = session.Commit().Error; err != nil {
		return err
	}

	_ = broker.SubSubject(member.RoleID, broker.QuitGuild, guildID)
	//_ = broker.GuildPublish(req.GuildID, pb.WsCode_CodeGuildChat, &pb.Chat{Content: member.Name + "退出公会"})

	// 记录事件
	models.CreateRecord(manager.Guild.ServerID, manager.Guild.GuildID, 2, member.Name, "")

	resp.Value = setQuitCD(member.RoleID)
	return nil
}

func (h *GuildServiceHandler) Dissolve(ctx context.Context, _ *empty.Empty, resp *wrappers.Int32Value) (err error) {

	var guildID = h.GetGuildID(ctx)
	if guildID == 0 {
		return errors.ParamError
	}

	manager, err := h.getManager(h.GetServerID(ctx), guildID)
	if err != nil {
		return err
	}

	manager.Lock()
	defer manager.Unlock()

	var me = manager.GetMember(h.GetRoleID(ctx))
	if me == nil {
		return errors.MemberError
	}
	if me.Position != pb.GuildPosition_President {
		return errors.PositionNoPermissionError
	}

	var session = database.DB.Begin()

	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()
		}
	}()

	if err = models.DelMemberByGuildID(session, guildID); err != nil {
		return err
	}
	if err = models.DelGuild(session, guildID); err != nil {
		return err
	}
	if err = session.Commit().Error; err != nil {
		return err
	}

	for _, v := range manager.GetMembers() {
		_ = broker.SubSubject(v.RoleID, broker.QuitGuild, guildID)
		//_ = broker.RolePublish(v.RoleID, broker.QuitGuild, &pb.Chat{Content: "某某解散了公会"})
	}

	h.Managers[h.GetServerID(ctx)].Del(guildID)

	resp.Value = setQuitCD(me.RoleID)
	return nil
}

//rpc SetElite (DefaultRequest) returns (pb.GuildMember) {}
//rpc SetVicePresident (DefaultRequest) returns (pb.GuildMember) {}
//rpc SetPresident (DefaultRequest) returns (pb.GuildMember) {}

const EliteMax = 4

// 设置精英
func (h *GuildServiceHandler) SetElite(ctx context.Context, req *wrappers.Int32Value, resp *pb.GuildMember) error {

	var guildID = h.GetGuildID(ctx)
	if guildID == 0 {
		return errors.ParamError
	}

	if h.GetRoleID(ctx) == req.Value {
		return errors.ParamError
	}

	var manager, err = h.getManager(h.GetServerID(ctx), guildID)
	if err != nil {
		return err
	}

	manager.Lock()
	defer manager.Unlock()

	var eliteNum = 0

	var member, me = manager.GetMember(req.Value), manager.GetMember(h.GetRoleID(ctx))

	for _, v := range manager.GetMembers() {
		if v.Position == pb.GuildPosition_Elite {
			eliteNum++
			if eliteNum >= EliteMax {
				return errors.MemberError
			}
		}
	}

	if me == nil {
		return errors.ParamError
	}
	if member == nil {
		return errors.MemberError
	}
	if member.Position == pb.GuildPosition_Elite {
		return nil
	}

	if me.Position < pb.GuildPosition_VicePresident || me.Position < member.Position {
		return errors.PositionNoPermissionError
	}

	*resp = *member

	resp.Position = pb.GuildPosition_Elite
	if err = models.SaveMember(database.DB, resp); err == nil {
		*member = *resp
	}

	//_ = broker.GuildPublish(resp.GuildID, pb.WsCode_CodeGuildChat, &pb.Chat{Content: me.Name + "任命" + member.Name + "为精英"})
	return nil
}

// 设置副会长
func (h *GuildServiceHandler) SetVicePresident(ctx context.Context, req *wrappers.Int32Value, resp *pb.GuildMember) error {

	var guildID = h.GetGuildID(ctx)
	if guildID == 0 {
		return errors.ParamError
	}

	if h.GetRoleID(ctx) == req.Value {
		return errors.ParamError
	}

	var manager, err = h.getManager(h.GetServerID(ctx), guildID)
	if err != nil {
		return err
	}

	manager.Lock()
	defer manager.Unlock()

	var member, me = manager.GetMember(req.Value), manager.GetMember(h.GetRoleID(ctx))

	for _, v := range manager.GetMembers() {

		if v.Position == pb.GuildPosition_VicePresident {
			// 已有副会长
			return nil
		}
	}

	if me == nil {
		return errors.ParamError
	}
	if member == nil {
		return errors.MemberError
	}
	if me.Position != pb.GuildPosition_President {
		return errors.PositionNoPermissionError
	}

	*resp = *member

	resp.Position = pb.GuildPosition_VicePresident
	if err = models.SaveMember(database.DB, resp); err == nil {
		*member = *resp
	}

	//_ = broker.GuildPublish(resp.GuildID, pb.WsCode_CodeGuildChat, &pb.Chat{Content: me.Name + "任命" + member.Name + "为副会长"})

	return nil
}

// 转移会长
func (h *GuildServiceHandler) SetPresident(ctx context.Context, req *wrappers.Int32Value, resp *pb.GuildMember) error {

	var guildID = h.GetGuildID(ctx)
	if guildID == 0 {
		return errors.ParamError
	}

	if h.GetRoleID(ctx) == req.Value {
		return errors.ParamError
	}

	var manager, err = h.getManager(h.GetServerID(ctx), guildID)
	if err != nil {
		return err
	}

	manager.Lock()
	defer manager.Unlock()

	var member, me = manager.GetMember(req.Value), manager.GetMember(h.GetRoleID(ctx))

	if me == nil {
		return errors.ParamError
	}
	if member == nil {
		return errors.MemberError
	}
	if me.Position != pb.GuildPosition_President {
		return errors.PositionNoPermissionError
	}

	if err := h.setPresident(manager.Guild, me, member); err != nil {
		return err
	}

	//_ = broker.GuildPublish(req.GuildRequest.GuildID, pb.WsCode_CodeGuildChat, &pb.Chat{Content: me.Name + "将会长转移给" + member.Name})

	*resp = *member
	return nil
}

// 降级为普通成员
func (h *GuildServiceHandler) SetMember(ctx context.Context, req *wrappers.Int32Value, resp *pb.GuildMember) error {

	var guildID = h.GetGuildID(ctx)
	if guildID == 0 {
		return errors.ParamError
	}

	if h.GetRoleID(ctx) == req.Value {
		return errors.ParamError
	}

	var manager, err = h.getManager(h.GetServerID(ctx), guildID)
	if err != nil {
		return err
	}

	manager.Lock()
	defer manager.Unlock()

	var member, me = manager.GetMember(req.Value), manager.GetMember(h.GetRoleID(ctx))

	if me == nil {
		return errors.ParamError
	}
	if member == nil {
		return errors.MemberError
	}

	if member.Position == pb.GuildPosition_Member {
		return nil
	}

	if me.Position <= member.Position {
		return errors.PositionNoPermissionError
	}

	*resp = *member
	resp.Position = pb.GuildPosition_Member
	if err = models.SaveMember(database.DB, resp); err == nil {
		*member = *resp
		//_ = broker.GuildPublish(resp.GuildID, pb.WsCode_CodeGuildChat, &pb.Chat{Content: me.Name + "任命" + member.Name + "为普通成员"})
	}

	return nil
}

// 踢人出公会
func (h *GuildServiceHandler) Kick(ctx context.Context, req *wrappers.Int32Value, _ *empty.Empty) (err error) {

	var guildID = h.GetGuildID(ctx)
	if guildID == 0 {
		return errors.ParamError
	}

	if h.GetRoleID(ctx) == req.Value {
		return errors.ParamError
	}

	manager, err := h.getManager(h.GetServerID(ctx), guildID)
	if err != nil {
		return err
	}

	manager.Lock()
	defer manager.Unlock()

	var member, me = manager.GetMember(req.Value), manager.GetMember(h.GetRoleID(ctx))

	if me == nil {
		return errors.ParamError
	}
	if member == nil {
		return errors.MemberError
	}

	if me.Position < pb.GuildPosition_VicePresident || me.Position <= member.Position {
		// 副会长以上才有权限
		return errors.PositionNoPermissionError
	}

	var guildCopy = *manager.Guild

	var session = database.DB.Begin()

	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()

			*manager.Guild = guildCopy
			manager.AddMember(member)
		}
	}()

	manager.DelMember(member)
	models.SaveGuild(session, manager.Guild)

	if err = models.DelMember(session, member.RoleID); err != nil {
		return err
	}
	if err = session.Commit().Error; err != nil {
		return err
	}

	_ = broker.SubSubject(member.RoleID, broker.QuitGuild, guildID)
	//_ = broker.GuildPublish(req.GuildRequest.GuildID, pb.WsCode_CodeGuildChat, &pb.Chat{Content: me.Name + "将" + member.Name + "提出公会"})

	// 记录事件
	models.CreateRecord(manager.Guild.ServerID, manager.Guild.GuildID, 3, member.Name, me.Name)

	return nil
}

// 发送入会申请
func (h *GuildServiceHandler) Apply(_ context.Context, req *service.GuildJoinRequest, _ *empty.Empty) error {

	var _, ok = models.GetMember(database.DB, req.Role.RoleID)
	if !ok {

		var _, err = h.getManager(req.Role.ServerID, req.GuildID)
		if err != nil {
			return err
		}

		if !models.FindApplyByRoleID(database.DB, req.Role.RoleID, req.GuildID) {
			return models.CreateApply(database.DB, req.Role, req.GuildID)
		}
	}

	return nil
}

func (h *GuildServiceHandler) GetApplies(ctx context.Context, page *wrappers.Int32Value, resp *pb.Roles) error {

	var guildID = h.GetGuildID(ctx)
	if guildID == 0 {
		return errors.ParamError
	}

	var res = models.GetApplies(database.DB, guildID, page.Value, 20)
	resp.Roles = make([]*pb.Role, 0, len(res))

	for _, v := range res {
		resp.Roles = append(resp.Roles, &pb.Role{
			RoleID: v.RoleID,
			Name:   v.RoleName,
			Level:  v.Level,
			Power:  v.Power,
			VIP:    v.VIP,
			Photo:  v.Photo,
			UserID: v.ID,
		})
	}

	return nil
}

// 服务器自用
func (h *GuildServiceHandler) GetAppliesByID(_ context.Context, req *pb.Int32Slice, resp *pb.Roles) error {

	var res = models.GetAppliesByID(database.DB, req.Slices)
	resp.Roles = make([]*pb.Role, 0, len(res))

	for _, v := range res {
		resp.Roles = append(resp.Roles, &pb.Role{
			RoleID: v.RoleID,
			UserID: v.ID,
			Name:   v.RoleName,
			Level:  v.Level,
			Power:  v.Power,
			VIP:    v.VIP,
			Photo:  v.Photo,
		})
	}

	return nil
}

// 删除入会申请
func (h *GuildServiceHandler) DelApply(_ context.Context, req *service.GuildDelApplyRequest, _ *empty.Empty) error {

	var manager, err = h.getManager(req.GuildRequest.ServerID, req.GuildRequest.GuildID)
	if err != nil {
		return err
	}

	var me *pb.GuildMember
	for _, v := range manager.GetMembers() {

		if v.RoleID == req.GuildRequest.RoleID {
			me = v
		}
	}

	if me == nil {
		return errors.ParamError
	}

	if me.Position < pb.GuildPosition_VicePresident {
		return errors.PositionNoPermissionError
	}

	return models.DelApply(database.DB, req.GuildRequest.GuildID, req.IDs)
}

// 建设公会
func (h *GuildServiceHandler) Build(ctx context.Context, req *pb.GuildBuildRequest, resp *pb.GuildBuildReply) (err error) {

	var roleID, serverID, guildID = h.GetRoleID(ctx), h.GetServerID(ctx), h.GetGuildID(ctx)

	var manager *Manager
	manager, err = h.getManager(serverID, guildID)
	if err != nil {
		return err
	}

	var buildConf, ok = buildConfigs[req.BuildType]
	if !ok {
		return errors.New("Option ID error")
	}

	manager.Lock()
	defer manager.Unlock()

	var me *pb.GuildMember
	for _, v := range manager.GetMembers() {

		if v.RoleID == roleID {
			me = v
		}
	}

	if me == nil || me.BuildDate == date.Date(time.Now()) {
		return errors.ParamError
	}

	var points = buildConf.Points
	var item = itemC.AddItem(me.RoleID, consts.Guild, points)

	var session = database.DB.Begin()
	if session.Error != nil {
		return session.Error
	}

	var guildCopy = *manager.Guild
	var memberCopy = *me

	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()

			*manager.Guild = guildCopy
			*me = memberCopy
		}
	}()

	var exp = buildConf.Exp
	if lvUp := manager.Guild.AddExp(exp); lvUp {
		// 公会升级：[公会名称]活跃非凡，提升至[公会等级]级
		_ = broker.ServerPublish(manager.Guild.ServerID, pb.WsCode_CodeMessage, &pb.ServerMessage{Type: 6, Name: []string{manager.Guild.Name}, Nums: []int32{manager.Guild.Level}})
	}

	var money = buildConf.Money
	manager.Guild.Money += money
	models.SaveGuild(session, manager.Guild)

	me.Points = item.Number
	me.Points2 += points
	me.BuildType = req.BuildType
	me.BuildDate = date.Date(time.Now())
	if err = models.SaveMember(session, me); err != nil {
		return err
	}

	if err = session.Commit().Error; err != nil {
		return err
	}

	resp.Member = me
	resp.Guild = &pb.Guild{}
	*resp.Guild = models.BuildPbGuild(manager.Guild)

	// 公会日常任务
	_ = addDailyQuestsPoints(manager.Guild.GuildID, money)

	resp.DailyQuests = getDailyQuestsData(guildID).DailyQuests

	go func() {

		defer func() {
			_ = recover()
		}()

		questC.DailyGuildBuild(roleID)
		activityC.AddGuildPoints(resp.Guild, activityC.GuildExp, exp)
		// 记录事件
		models.CreateRecord(manager.Guild.ServerID, manager.Guild.GuildID, 6, me.Name, "", int32(req.BuildType), exp, buildConf.Money, points)
	}()

	return nil
}

func (h *GuildServiceHandler) Online(_ context.Context, req *service.GuildRequest, _ *empty.Empty) error {

	var manager, err = h.getManager(req.ServerID, req.GuildID)
	if err != nil {
		return err
	}

	_ = models.UpdateOnlineTime(database.DB, req.RoleID)
	manager.Online(req.RoleID)
	return nil
}

func (h *GuildServiceHandler) Offline(_ context.Context, req *service.GuildRequest, _ *empty.Empty) error {

	var manager = h.Managers[req.ServerID].Get(req.GuildID)
	if manager == nil {
		return nil
	}

	manager.Offline(req.RoleID)

	if manager.AllOffline() {

		// 会长7天未上线 转移会长
		if time.Now().Unix()-manager.GetPresident().OnlineTime > 8*86400 {

			var newPresident *pb.GuildMember

			for _, m := range manager.GetMembers() {
				if m.Position != pb.GuildPosition_President {
					if newPresident == nil {
						newPresident = m
					}
					if m.Position > newPresident.Position {
						newPresident = m
					} else if m.Position == newPresident.Position && m.Points2 > newPresident.Points2 {
						newPresident = m
					}
				}
			}

			if newPresident != nil {
				//_ = h.setPresident(manager.Guild, manager.GetPresident(), newPresident)
			}

		}

		h.Managers[req.ServerID].Del(req.GuildID)

		//
	}

	return nil
}

//func (h *GuildServiceHandler) BuildConfigs(_ context.Context, req *empty.Empty, resp *service.GuildBuildConfigsResponse) error {
//
//	resp.BuildConfigs = make([]*pb.GuildBuildConf, 0, len(buildConfigs))
//	for _, buildConf := range buildConfigs {
//		resp.BuildConfigs = append(resp.BuildConfigs, buildConf)
//	}
//
//	return nil
//}

func (h *GuildServiceHandler) GetNotice(_ context.Context, req *service.GuildRequest, resp *wrappers.StringValue) error {

	var _, err = h.getManager(req.ServerID, req.GuildID)
	if err != nil {
		return err
	}

	return database.DB.Model(&entity.Notice{GuildID: req.GuildID}).Where("guild_id = ?", req.GuildID).Select("notice AS \"value\"").Scan(resp).Error
}

func (h *GuildServiceHandler) GetBosses(ctx context.Context, _ *empty.Empty, resp *pb.GuildBosses) error {

	var manager, err = h.getManager(h.GetServerID(ctx), h.GetGuildID(ctx))
	if err != nil {
		return err
	}

	var bosses = models.GetBosses(database.DB, h.GetGuildID(ctx), manager.Guild.Level)

	resp.IDs = models.GetBossCaptainIDs(h.GetRoleID(ctx))
	resp.Bosses = make([]*pb.GuildBoss, 0, len(bosses))
	for _, v := range bosses {
		resp.Bosses = append(resp.Bosses, &pb.GuildBoss{
			BossID: v.BossID,
			Open:   v.Open,
			HP:     v.HP,
		})
	}

	return nil
}

func (h *GuildServiceHandler) GetBoss(ctx context.Context, req *wrappers.Int32Value, resp *pb.GuildBoss) error {

	var boss, err = models.GetBoss(database.DB, h.GetGuildID(ctx), req.Value)
	if err != nil {
		return err
	}

	conf, err := configuration.GetBoss(req.Value)
	if err != nil {
		return err
	}

	*resp = pb.GuildBoss{
		BossID:  boss.BossID,
		Open:    boss.Open,
		HP:      boss.HP,
		AwardID: conf.RandomAwardID,
	}

	return nil
}

func (h *GuildServiceHandler) OpenBoss(ctx context.Context, req *wrappers.Int32Value, resp *pb.GuildBoss) error {

	var manager, err = h.getManager(h.GetServerID(ctx), h.GetGuildID(ctx))
	if err != nil {
		return err
	}

	var me = manager.GetMember(h.GetRoleID(ctx))
	if me == nil {
		return errors.ParamError
	}

	if me.Position < pb.GuildPosition_VicePresident {
		return errors.PositionNoPermissionError
	}

	manager.Lock()
	defer manager.Unlock()

	boss, err := models.GetBoss(database.DB, me.GuildID, req.Value)
	if err != nil {
		return err
	}

	conf, err := configuration.GetBoss(boss.BossID)
	if err != nil {
		return err
	}

	boss.HP = conf.BossHP
	boss.Open = true

	if manager.Guild.Money < conf.CostGuildMoney {
		return errors.ParamError
	}

	var guildCopy = *manager.Guild

	defer func() {
		if r := recover(); r != nil {
			*manager.Guild = guildCopy
		}
	}()

	manager.Guild.Money -= conf.CostGuildMoney
	models.SaveGuild(database.DB, manager.Guild)

	// 记录事件
	models.CreateRecord(manager.Guild.ServerID, manager.Guild.GuildID, 4, me.Name, "", boss.BossID)

	resp.BossID = boss.BossID
	resp.Open = boss.Open
	resp.HP = boss.HP

	return database.DB.Save(&boss).Error
}

func (h *GuildServiceHandler) AttackBoss(ctx context.Context, req *service.GuildAttackBossRequest, resp *pb.GuildAttackBossResp) error {

	var manager, err = h.getManager(h.GetServerID(ctx), h.GetGuildID(ctx))
	if err != nil {
		return err
	}

	var roleID = h.GetRoleID(ctx)

	manager.Lock()
	defer manager.Unlock()

	var guild = manager.Guild
	var bosses = models.GetBosses(database.DB, guild.GuildID, manager.Guild.Level)

	var boss entity.Boss

	for _, v := range bosses {
		if v.BossID == req.BossID {
			boss = v
		}
	}

	if boss.BossID == 0 {
		// boss not found
		return errors.ParamError
	}

	//boss, err := models.GetBoss(database.DB, req.GuildRequest.GuildID, req.BossID)
	//if err != nil {
	//	return err
	//}

	if !boss.Open {
		return errors.ParamError
	}

	if boss.GetHP() == 0 {
		resp.Success = false
		return nil
	}

	resp.IDs = models.GetBossCaptainIDs(roleID)
	if v := resp.IDs[req.CaptainID]; v == 1 || v == 3 {
		return errors.ParamError
	}

	resp.IDs[req.CaptainID] = resp.IDs[req.CaptainID] + 1
	models.SetBossCaptainIDs(roleID, resp.IDs)

	conf, err := configuration.GetBoss(boss.BossID)
	if err != nil {
		return err
	}

	//单次伤害获得公会贡献 = MAX ( 总公会贡献 * 船长伤害比例 /( 船长伤害比例 + 9 ), 1)
	var rate = float64(req.Damage) / float64(conf.BossHP)
	var points = int32(math.Max(float64(conf.CouponGuild)*rate/(rate+9), 1))

	boss.HP -= req.Damage
	if boss.HP <= 0 {
		boss.HP = 0
	}

	var me = manager.GetMember(roleID)
	var meCopy = *me

	var item = itemC.AddItem(me.RoleID, consts.Guild, points)

	meCopy.Points = item.Number
	meCopy.Points2 += points
	if err = models.SaveMember(database.DB, &meCopy); err != nil {
		return err
	} else {
		*me = meCopy
	}

	var msg = entity.BossRecord{
		GuildID:   guild.GuildID,
		Name:      me.Name,
		BossID:    req.BossID,
		RoleID:    roleID,
		CaptainID: req.CaptainID,
		Damage:    req.Damage,
		Points:    points,
	}

	// 先写入攻击记录
	database.DB.Create(&msg)

	// 消息推送
	_ = broker.GuildPublish(guild.GuildID, pb.WsCode_CodeGBossMsg, buildPbBossMsg(msg))

	if boss.HP == 0 {
		// 击杀奖励
		resp.Award = awardM.RandAward(&pb.Role{RoleID: h.GetRoleID(ctx)}, conf.KillAwardID)
		resp.Exp = conf.GuildEXP

		boss.InitAwards(models.CountByBossRecordRoleID(database.DB, guild.GuildID, boss.BossID))

		var guildCopy = *guild

		defer func() {
			if r := recover(); r != nil {
				*guild = guildCopy
			}
		}()

		if lvUp := guild.AddExp(conf.GuildEXP); lvUp {
			// 公会升级：[公会名称]活跃非凡，提升至[公会等级]级
			_ = broker.ServerPublish(guild.ServerID, pb.WsCode_CodeMessage, &pb.ServerMessage{Type: 6, Name: []string{guild.Name}, Nums: []int32{guild.Level}})
		}

		models.SaveGuild(database.DB, guild)

		go func() {

			defer func() {
				_ = recover()
			}()

			// 记录事件
			models.CreateRecord(manager.Guild.ServerID, manager.Guild.GuildID, 5, me.Name, "", boss.BossID)

			var mess = models.BuildPbGuild(manager.Guild)
			activityC.AddGuildPoints(&mess, activityC.GuildExp, resp.Exp)
		}()
	}

	_ = broker.RoomPublish("GBoss", guild.GuildID, pb.WsCode_CodeLBossMsg, buildPbBossMsg(msg))

	resp.Success = true
	resp.Points = points
	resp.HP = boss.HP

	return models.SaveBoss(database.DB, &boss)
}

func (h *GuildServiceHandler) Ranking(ctx context.Context, req *wrappers.Int32Value, resp *service.GuildBossRecords) error {

	resp.GuildBossRecords = make([]*service.GuildBossRecord, 0)

	var now = time.Now()
	var dateTime = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)

	return database.DB.Table("boss_records").
		Where("guild_id = ?", h.GetGuildID(ctx)).Where("boss_id = ?", req.Value).Where("created_at > ?", dateTime).
		Select("role_id, SUM(damage) AS damage, SUM(points) AS points, MAX(created_at) AS created_at").
		Group("role_id").Order("SUM(damage) DESC").Order("MAX(created_at) ASC").Scan(&resp.GuildBossRecords).Error
}

// 查看BOSS奖励领取情况
func (h *GuildServiceHandler) BossAwards(ctx context.Context, req *wrappers.Int32Value, resp *pb.GuildBossAwardResp) error {

	var boss, err = models.GetBoss(database.DB, h.GetGuildID(ctx), req.Value)
	if err != nil {
		return err
	}

	resp.GuildBossAwards = boss.Awards
	return nil
}

// 领取BOSS奖励
func (h *GuildServiceHandler) SetBossAward(ctx context.Context, req *service.SetBossAwardRequest, _ *empty.Empty) error {

	if _, err := h.getManager(h.GetServerID(ctx), h.GetGuildID(ctx)); err != nil {
		return err
	}

	var boss, err = models.GetBoss(database.DB, h.GetGuildID(ctx), req.BossID)
	if err != nil {
		return err
	}

	for k, v := range boss.Awards {
		if v.Role != nil && v.Role.RoleID == req.Award.Role.RoleID {
			return errors.ParamError
		}
		if k+1 == int(req.Index) && v.Role != nil {
			return errors.ParamError
		}
	}

	*boss.Awards[req.Index-1] = *req.Award
	_ = boss.BeforeSave()
	return models.SaveBoss(database.DB, &boss)
}

func (h *GuildServiceHandler) Resurrection(ctx context.Context, req *wrappers.Int32Value, resp *pb.ResurrectionResp) error {

	var roleID = h.GetRoleID(ctx)

	resp.IDs = models.GetBossCaptainIDs(roleID)

	// 101051 海之号角
	if err := itemC.AsyncDelItem(h.GetRoleID(ctx), h.GetServerID(ctx), 101051, 1); err != nil {
		return err
	}

	if v := resp.IDs[req.Value]; v == 1 {
		resp.IDs[req.Value] = 2
		models.SetBossCaptainIDs(roleID, resp.IDs)
	}

	return nil
}

func (h *GuildServiceHandler) AttackMessages(ctx context.Context, _ *empty.Empty, resp *pb.GuildAttackMessages) error {

	var year, month, day = time.Now().Date()

	var data []entity.BossRecord
	database.DB.Where("guild_id = ?", h.GetGuildID(ctx)).Where("created_at >= ?", time.Date(year, month, day, 0, 0, 0, 0, time.Local)).Order("id DESC").Limit(100).Find(&data)

	resp.Messages = make([]*pb.GuildAttackMsg, 0, len(data))
	for _, v := range data {
		resp.Messages = append(resp.Messages, buildPbBossMsg(v))
	}

	return nil
}

func (h *GuildServiceHandler) Record(ctx context.Context, _ *empty.Empty, resp *pb.GuildRecords) error {

	var data []entity.GuildRecord

	if guildID := h.GetGuildID(ctx); guildID == 0 {

		database.DB.Where(entity.GuildRecord{ServerID: h.GetServerID(ctx), Type: 1}).Order("id DESC").Limit(20).Find(&data)

	} else {

		database.DB.Where("guild_id = ?", guildID).Order("id DESC").Limit(50).Find(&data)
	}

	resp.Records = make([]*pb.GuildRecord, 0, len(data))
	for _, v := range data {
		resp.Records = append(resp.Records, &pb.GuildRecord{
			Type:   int32(v.Type),
			Name:   v.Name,
			Name2:  v.Name2,
			Values: v.Values,
		})
	}

	return nil
}

func (h *GuildServiceHandler) GetSkills(ctx context.Context, _ *empty.Empty, resp *pb.GuildSkills) error {

	var roleID, guildID = h.GetRoleID(ctx), h.GetGuildID(ctx)

	if guildID > 0 {
		var manager, err = h.getManager(h.GetServerID(ctx), guildID)
		if err != nil {
			return err
		}

		var data = models.GetSkills(roleID)

		resp.Skills = make([]*pb.GuildSkill, 0, len(data))
		for _, v := range data {

			resp.Skills = append(resp.Skills, buildPbSkill(v, manager.Guild.Level))
		}
	}

	return nil
}

func (h *GuildServiceHandler) UpSkill(ctx context.Context, req *wrappers.Int32Value, resp *pb.GuildUpSkillResp) error {

	var roleID, serverID = h.GetRoleID(ctx), h.GetServerID(ctx)

	var skill = entity.GuildSkill{
		RoleID:  roleID,
		SkillID: req.Value,
	}
	if err := database.DB.First(&skill).Error; err != nil {
		return err
	}

	var conf = configuration.GetSkill(skill.SkillID, skill.Level)
	if conf.CostCount == 0 {
		return fmt.Errorf("conf.CostCount == 0")
	}

	var item, err = itemC.DelItem(roleID, serverID, consts.Guild, conf.CostCount)
	if err != nil {
		return err
	}

	manager, err := h.getManager(serverID, h.GetGuildID(ctx))
	if err != nil {
		return err
	}

	manager.Lock()
	defer manager.Unlock()

	var me *pb.GuildMember
	for _, v := range manager.GetMembers() {
		if v.RoleID == roleID {
			me = v
		}
	}

	if me == nil {
		return fmt.Errorf("me == nil")
	}

	me.Points = item.Number
	if err = models.SaveMember(database.DB, me); err != nil {
		return err
	}

	skill.Level += 1
	database.DB.Save(&skill)

	conf = configuration.GetSkill(skill.SkillID, skill.Level)

	resp.Points = me.Points
	resp.Skill = buildPbSkill(skill, manager.Guild.Level)

	_ = broker.RolePublish(roleID, pb.WsCode_CodeItem, item)

	if conf.Camp > 0 {
		go captainC.CaptainService.UpdateByCamp(ctx, &wrappers.Int32Value{Value: conf.Camp})
	}
	return nil
}

func (h *GuildServiceHandler) GetSkillByShipID(ctx context.Context, req *wrappers.Int32Value, resp *wrappers.FloatValue) error {

	var skills pb.GuildSkills
	if err := h.GetSkills(ctx, &empty.Empty{}, &skills); err != nil {
		return err
	}

	for _, skill := range skills.Skills {
		var conf = configuration.GetSkill(skill.Skill.ID, skill.Skill.Lv)
		if conf.ShipID == req.Value {

			resp.Value = skill.Value
			return nil
		}
	}

	return errors.ParamError
}

func (h *GuildServiceHandler) GetSkills2(_ context.Context, req *wrappers.Int32Value, resp *pb.GuildSkills) error {

	var roleID = req.Value

	var member, ok = models.GetMember(database.DB, roleID)
	if !ok {
		return nil
	}

	var guild = entity.Guild{GuildID: member.GuildID}
	database.DB.First(&guild)

	var data = models.GetSkills(roleID)

	resp.Skills = make([]*pb.GuildSkill, 0, len(data))
	for _, v := range data {

		resp.Skills = append(resp.Skills, buildPbSkill(v, guild.Level))
	}

	return nil

}

func (h *GuildServiceHandler) getManager(serverID, guildID int32) (*Manager, error) {

	var manager = h.Managers[serverID].Get(guildID)
	if manager == nil {
		var guild, ok = models.GetGuild(database.DB, serverID, guildID)
		if !ok {
			return nil, fmt.Errorf("ID %d Guild not found", guildID)
		}

		manager = NewManager(guild, models.GetMembers(database.DB, guildID))
		manager.Work(h.work)
		h.Managers[serverID].Add(guildID, manager)
	}

	return manager, nil
}

// 同步成员战力函数
func (h *GuildServiceHandler) work(manager *Manager) {

	defer func() {
		_ = recover()
	}()

	var roleIds []int32
	for _, v := range manager.GetMembers() {
		roleIds = append(roleIds, v.RoleID)
	}

	var roles = roleC.GetRoles(roleIds)
	for _, member := range manager.GetMembers() {
		mergeRole(member, roles[member.RoleID])
	}

	if manager.Guild.Power != manager.GetPower() {

		manager.Lock()
		defer manager.Unlock()

		var guildCopy = *manager.Guild

		defer func() {
			if r := recover(); r != nil {
				*manager.Guild = guildCopy
			}
		}()

		manager.Guild.Power = manager.GetPower()

		models.SaveGuild(database.DB, manager.Guild)
	}
}

func (h *GuildServiceHandler) setPresident(guild *entity.Guild, old, new *pb.GuildMember) (err error) {

	var guildCopy, oldCopy, newCopy = *guild, *old, *new

	var session = database.DB.Begin()

	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()

			*guild = guildCopy
			*old = oldCopy
			*new = newCopy
		}
	}()

	old.Position = pb.GuildPosition_Member
	if err = models.SaveMember(session, old); err != nil {
		return err
	}

	new.Position = pb.GuildPosition_President
	if err = models.SaveMember(session, new); err != nil {
		return err
	}

	guild.PresidentName = new.Name
	models.SaveGuild(session, guild)

	return session.Commit().Error
}

// 角色的属性合并到成员上
func mergeRole(member *pb.GuildMember, role *pb.Role) *pb.GuildMember {

	member.Name = role.Name
	member.Level = role.Level
	member.VIP = role.VIP
	member.Power = role.Power
	return member
}

//初级建设	钻石*10	公会经验+10、公会资金+10、个人贡献+10
//中级建设	钻石*50	公会经验+50、公会资金+50、个人贡献+50
//高级建设	钻石*200	公会经验+200、公会资金+200、个人贡献+200
//道具建设	公会设计图	公会经验+400、公会资金+400、个人贡献+400
//高级道具建设	高级公会设计图	公会经验+1200、公会资金+1200、个人贡献+1200

var buildConfigs = map[pb.GuildBuildType]*pb.GuildBuildConf{
	1: {
		BuildType: pb.GuildBuildType_GuildBuildType1, Exp: 10, Money: 10, Points: 10,
		Item: &pb.RawItem{ItemID: consts.Diamond, Number: 10},
	},
	2: {
		BuildType: pb.GuildBuildType_GuildBuildType2, Exp: 50, Money: 50, Points: 50,
		Item: &pb.RawItem{ItemID: consts.Diamond, Number: 50},
	},
	3: {
		BuildType: pb.GuildBuildType_GuildBuildType3, Exp: 200, Money: 200, Points: 200,
		Item: &pb.RawItem{ItemID: consts.Diamond, Number: 200},
	},
	4: {
		BuildType: pb.GuildBuildType_GuildBuildType4, Exp: 400, Money: 400, Points: 400,
		Item: &pb.RawItem{ItemID: 101041, Number: 1},
	},
	5: {
		BuildType: pb.GuildBuildType_GuildBuildType5, Exp: 1200, Money: 1200, Points: 1200,
		Item: &pb.RawItem{ItemID: 101042, Number: 1},
	},
}

func quitCD(roleID int32) int32 {

	var key = fmt.Sprintf("GuildQuitTime%d", roleID)
	if b, err := redis.Get(key); err != nil {
		panic(err)

	} else if b != nil {

		var Time, _ = time.Parse(time.RFC3339Nano, string(b))
		return int32(math.Max(0, Time.Add(time.Hour*24).Sub(time.Now()).Seconds()))

	} else {

		return 0
	}
}

func setQuitCD(roleID int32) int32 {

	var key = fmt.Sprintf("GuildQuitTime%d", roleID)
	var str = time.Now().Format(time.RFC3339Nano)

	_ = redis.Set(key, []byte(str), 86400)
	return 86400
}

func buildPbBossMsg(data entity.BossRecord) *pb.GuildAttackMsg {

	return &pb.GuildAttackMsg{
		Name:      data.Name,
		CaptainID: data.CaptainID,
		BossID:    data.BossID,
		Damage:    data.Damage,
		Timestamp: data.CreatedAt.Unix(),
	}
}

func buildPbSkill(data entity.GuildSkill, guildLevel int32) *pb.GuildSkill {

	var skill = &pb.Skill{ID: data.SkillID, Lv: data.Level}

	var value float32
	if skill.Lv > guildLevel {
		value = configuration.GetSkill(skill.ID, guildLevel).Rate
	} else {
		value = configuration.GetSkill(skill.ID, skill.Lv).Rate
	}

	var conf = configuration.GetSkill(skill.ID, skill.Lv)

	return &pb.GuildSkill{
		Skill:      skill,
		CostPoints: conf.CostCount,
		Value:      value,
		Camp:       conf.Camp}
}
